package service

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"

	"gitee.com/jn-shao/common"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"podSrv/domain/model"
	"podSrv/domain/repository"
	"podSrv/proto/podSrv"
)

// 这里是接口类型
type IPodDataService interface {
	AddPod(*model.Pod) (int64, error)
	DeletePod(int64) error
	UpdatePod(*model.Pod) error
	FindPodByID(int64) (*model.Pod, error)
	FindPodList(pod *model.Page) (int64, []model.Pod, error)
	CreateToK8s(*podSrv.PodInfo) error
	DeleteFromK8s(*model.Pod) error
	UpdateToK8s(*podSrv.PodInfo) error
}

type PodDataService struct {
	// 注意：这里是 IPodRepository 类型
	PodRepository repository.IPodRepository
	// 相当于kubectl与k8s交互
	K8sClientSet *kubernetes.Clientset
	// Deployment
	deployment *appsv1.Deployment
}

// 创建
// 注意：返回值 IPodDataService 接口类型
func NewPodDataService(podRepository repository.IPodRepository, clientSet *kubernetes.Clientset) IPodDataService {
	return &PodDataService{
		PodRepository: podRepository,
		K8sClientSet:  clientSet,
		deployment:    &appsv1.Deployment{},
	}
}

// 添加 Pod
func (p *PodDataService) AddPod(pod *model.Pod) (int64, error) {
	return p.PodRepository.CreatePod(pod)
}

// 删除 Pod
func (p *PodDataService) DeletePod(podID int64) error {
	return p.PodRepository.DeletePodByID(podID)
}

// 更新 Pod
func (p *PodDataService) UpdatePod(pod *model.Pod) error {
	return p.PodRepository.UpdatePod(pod)
}

// 单个 Pod 查找
func (p *PodDataService) FindPodByID(podID int64) (*model.Pod, error) {
	return p.PodRepository.FindPodByID(podID)
}

// 查找所有 Pod
func (p *PodDataService) FindPodList(page *model.Page) (int64, []model.Pod, error) {
	return p.PodRepository.FindPodList(page)
}

// 创建 pod 到 k8s 中
func (p *PodDataService) CreateToK8s(podInfo *podSrv.PodInfo) error {
	p.setDeployment(podInfo)
	// 查看 deployment 是否存在
	if _, err := p.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Get(context.TODO(), podInfo.PodName, metav1.GetOptions{}); err != nil {
		// 不存在，创建 deployment
		if _, err = p.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Create(context.TODO(), p.deployment, metav1.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("创建成功")
		return nil

	} else {
		// 存在，可以写自己的业务逻辑
		common.Error("Pod " + podInfo.PodName + "已经存在")
		return errors.New("Pod " + podInfo.PodName + " 已经存在")
	}
}

// 删除 pod
func (p *PodDataService) DeleteFromK8s(pod *model.Pod) error {
	// 删除 deployment
	if err := p.K8sClientSet.AppsV1().Deployments(pod.PodNamespace).Delete(context.TODO(), pod.PodName, metav1.DeleteOptions{}); err != nil {
		common.Error(err)
		//写自己的业务逻辑
		return err
	}
	common.Info(pod.PodName + " 删除成功")
	return nil
}

// 更新 deployment，pod
func (p *PodDataService) UpdateToK8s(podInfo *podSrv.PodInfo) error {
	p.setDeployment(podInfo)
	// 查看 deployment 是否存在
	if _, err := p.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Get(context.TODO(), podInfo.PodName, metav1.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("Pod " + podInfo.PodName + " 不存在请先创建")
	} else {
		// 如果存在
		if _, err = p.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Update(context.TODO(), p.deployment, metav1.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
	}
	common.Info(podInfo.PodName + " 更新成功")
	return nil
}

func (p *PodDataService) setDeployment(podInfo *podSrv.PodInfo) {
	deployment := &appsv1.Deployment{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Deployment",
			APIVersion: "apps/v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      podInfo.PodName,
			Namespace: podInfo.PodNamespace,
			Labels: map[string]string{
				"app-name": podInfo.PodName,
				"author":   "SHao",
			},
			Annotations: map[string]string{
				"k8s/generated-by": "go-paas",
			},
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: &podInfo.PodReplicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"app-name": podInfo.PodName,
				},
				MatchExpressions: nil,
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Name:      podInfo.PodName,
					Namespace: podInfo.PodNamespace,
					Labels: map[string]string{
						"app-name": podInfo.PodName,
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:            podInfo.PodName,
							Image:           podInfo.PodImage,
							Ports:           p.getContainerPort(podInfo),
							Env:             p.getEnv(podInfo),
							Resources:       p.getResources(podInfo),
							ImagePullPolicy: p.getImagePullPolicy(podInfo),
						},
					},
					RestartPolicy: corev1.RestartPolicy(podInfo.PodRestart),
				},
			},
			Strategy:                p.getDeploymentStrategy(podInfo),
			MinReadySeconds:         0,
			RevisionHistoryLimit:    nil,
			Paused:                  false,
			ProgressDeadlineSeconds: nil,
		},
	}

	p.deployment = deployment
	b, _ := json.Marshal(deployment)
	common.Info(string(b))
}

func (p *PodDataService) getContainerPort(podInfo *podSrv.PodInfo) (containerPort []corev1.ContainerPort) {
	for _, v := range podInfo.PodPort {
		containerPort = append(containerPort, corev1.ContainerPort{
			Name:          "port-" + strconv.FormatInt(int64(v.ContainerPort), 10),
			ContainerPort: v.ContainerPort,
			Protocol:      p.getProtocol(v.Protocol),
		})
	}
	return
}

func (p *PodDataService) getProtocol(protocol string) corev1.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}

func (p *PodDataService) getEnv(podInfo *podSrv.PodInfo) (envVar []corev1.EnvVar) {
	for _, v := range podInfo.PodEnv {
		envVar = append(envVar, corev1.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

func (p *PodDataService) getResources(podInfo *podSrv.PodInfo) (source corev1.ResourceRequirements) {
	//最大能够使用多少资源
	//cup核心数，小于1核心可使用单位m，1核心=1000m
	//默认Byte，内存单位E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki
	source.Limits = corev1.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(podInfo.PodCpuMax), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(podInfo.PodMemoryMax), 'f', 6, 64)),
	}
	//满足最少使用的资源量
	podCpuMin := podInfo.PodCpuMin
	if podCpuMin <= 0 || podCpuMin > podInfo.PodCpuMax {
		podCpuMin = podInfo.PodCpuMax
	}
	podMemoryMin := podInfo.PodMemoryMin
	if podMemoryMin <= 0 || podMemoryMin > podInfo.PodMemoryMax {
		podMemoryMin = podInfo.PodMemoryMax
	}
	source.Requests = corev1.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(podCpuMin), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(podMemoryMin), 'f', 6, 64)),
	}
	return
}

func (p *PodDataService) getImagePullPolicy(podInfo *podSrv.PodInfo) corev1.PullPolicy {
	switch podInfo.PodPullPolicy {
	case "Always":
		return "Always"
	case "Never":
		return "Never"
	case "IfNotPresent":
		return "IfNotPresent"
	default:
		return "Always"
	}
}

func (p *PodDataService) getDeploymentStrategy(podInfo *podSrv.PodInfo) appsv1.DeploymentStrategy {
	switch podInfo.PodType {
	case "Recreate":
		return appsv1.DeploymentStrategy{Type: "Recreate"}
	default:
		return appsv1.DeploymentStrategy{Type: "RollingUpdate"}
	}
}
